Skip to content

feat(export): add combined project, model and agent reporting - #1713

Merged
wesm merged 5 commits into
mainfrom
feat/joint-reporting-export
Sep 13, 2026
Merged

feat(export): add combined project, model and agent reporting#1713
wesm merged 5 commits into
mainfrom
feat/joint-reporting-export

Conversation

@wesm

@wesm wesm commented Sep 10, 2026

Copy link
Copy Markdown
Member

Reporting exports can preserve project, model and agent relationships at a chosen time resolution. Separate breakdowns cannot answer questions such as "how much did this model cost on this project?" Opt-in reporting schema 4 supplies those combined facts without session identifiers, messages or tool content.

  • Hour, day and digest exports accept repeatable --project-key selection and --bucket, such as 1m, 5m or 15m. Any positive whole-minute duration that divides an hour is valid. Five minutes remains the default; v4 documents declare bucket_seconds. Schema 3 remains the default and keeps its existing bytes and digests. Versions 1 and 2 are no longer supported.
  • Activity and usage cells share the Activity classification: interactive, subagent, or automated, with subagents taking precedence over the automation flag, including sessions whose usage falls after their activity range. Bucket duration does not change the inactivity gap cap or accounting rules. Usage deduplication and cost allocation precede project selection. Standalone usage remains unattributed, including when a real session has an empty project label. Known costs retain their pricing provenance; unknown pricing remains visible.
  • Each hour contains a complete replacement set. Scope, resolution and cells participate in its digest, so corrections and resolution changes replace old facts instead of adding another charge. Finer precision does not require more frequent uploads. Device bucket peaks tighten concurrency bounds, but do not establish exact selected or cross-device concurrency.

The synthetic SQLite benchmark compares one- and five-minute exports with overlapping sessions, model switches and two project counts. This adds aggregation and payload cost, not incremental history discovery: digest screening still computes full day exports.

The timeout-response test uses virtual time and waits for its handler to finish, removing its dependence on host scheduling.

@roborev-ci

roborev-ci Bot commented Sep 10, 2026

Copy link
Copy Markdown

roborev: Combined Review (fade53b)

Verdict: Changes require fixes for 1 finding.

Medium

  • internal/db/reporting_joint.go:24-25, 103-114: Standalone usage rows have no session and therefore an empty project, but projects[""] may contain a real archive-scoped key when any session has an empty project label. The scope filter can then include unattributed Cursor usage in that project, and jointReportingHour emits it with the fabricated project key. Track whether the usage row belongs to a known session; keep unknown/standalone rows on an empty project key and exclude them from any non-empty project scope. Only resolve projects[session.Project] for known sessions.

    Reported by: codex


Reviewers: 2 done | Synthesis: codex, 10s | Total: 10m48s

@roborev-ci

roborev-ci Bot commented Sep 11, 2026

Copy link
Copy Markdown

roborev: Combined Review (b567745)

No issues found.


Reviewers: 2 done | Synthesis: codex | Total: 13m50s

@wesm wesm self-assigned this Sep 11, 2026
@mariusvniekerk mariusvniekerk self-assigned this Sep 12, 2026
Independent project, model and agent totals cannot answer combined filters.
Add opt-in reporting v3 with session-free five-minute cells, cost provenance
and project-key selection. Use the shared Activity interval engine so these
facts follow the same activity rules as the report.

Select usage survivors and allocate authoritative costs before filtering
projects. Bind complete cell sets and scope into each hour digest so late
corrections replace prior usage instead of adding another charge. Retain the
device bucket peak to keep aggregate concurrency bounds useful.

Keep existing v1/v2 contracts and the v2 default unchanged. Document the
replacement and precision limits, and measure the extra aggregation and
payload cost with a synthetic SQLite benchmark.
Reporting integrations need to choose their time precision without changing
the hourly publication cadence. Accept whole-minute durations that divide
an hour, declare the resolution in v3 documents and bind it to their content
identity. Preserve the v1/v2 bytes and the independent inactivity-gap rule.

Keep standalone usage unattributed even when a real session has an empty
project label. A missing session must not borrow that project's identity or
contribute to its scoped totals.

Compare one- and five-minute exports on the same synthetic archives so finer
precision has a measured aggregation and payload cost.
@mariusvniekerk
mariusvniekerk force-pushed the feat/joint-reporting-export branch from b567745 to 7dac973 Compare September 12, 2026 17:28
@roborev-ci

roborev-ci Bot commented Sep 12, 2026

Copy link
Copy Markdown

roborev: Combined Review (7dac973)

Verdict: Changes require fixes for 1 finding.

Medium

  • internal/activity/joint.go:91-96; internal/db/reporting_joint.go:90-112: Joint cells derive automation solely from IsAutomated, ignoring the existing IsSubagent-takes-precedence classification, so subagent activity and usage are mislabeled and inconsistent with v3 activity totals. Carry delegation classification into the joint-cell schema or define an explicit subagent value, and derive activity and usage labels using the same precedence as SessionMeta.kind().

    Reported by: codex


Reviewers: 2 done | Synthesis: codex, 8s | Total: 10m58s

Joint exports grouped sessions by their automation flag alone, merging
subagent minutes, peaks and usage into interactive or automated cells.
Use the activity classifier for both interval grouping and usage labels
so delegation takes precedence and matching subagents share their own cell.
@roborev-ci

roborev-ci Bot commented Sep 12, 2026

Copy link
Copy Markdown

roborev: Combined Review (b400376)

Verdict: Changes require fixes for 1 finding.

Medium

  • internal/db/reporting_joint.go:101-109; internal/db/reporting_export.go:285-294: Joint usage cells call session.ActivityCategory(), but usage-only sessions loaded by reportingUsageSessionsFrom do not populate IsSubagent. When such a session is outside the activity-session range, its usage is incorrectly labeled interactive or automated instead of subagent. Select and scan s.relationship_type = 'subagent' into SessionMeta.IsSubagent for usage sessions.

    Reported by: codex


Reviewers: 2 done | Synthesis: codex, 8s | Total: 7m41s

Usage recorded after a session's activity range comes through a separate
session query. That query omitted delegation metadata, so joint exports
labeled subagents as interactive or automated despite the shared classifier.
Load the relationship flag with usage sessions so their labels retain
subagent precedence even when no activity falls in the reporting period.
Windows CI observed a successful response in the timeout case. The test
used competing wall-clock timers, leaving deadline assertions dependent
on host scheduling. Advance the deadline and handler delay with Go's
virtual clock while retaining the response status, headers and body checks.
Join the handler before leaving the clock bubble after a timeout response.
@roborev-ci

roborev-ci Bot commented Sep 12, 2026

Copy link
Copy Markdown

roborev: Combined Review (15e0767)

Verdict: No findings at or above medium severity.


Reviewers: 2 done | Synthesis: codex, 8s | Total: 12m0s

@wesm
wesm merged commit 78da66d into main Sep 13, 2026
16 checks passed
@wesm
wesm deleted the feat/joint-reporting-export branch September 13, 2026 18:50
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

2 participants